import { Register } from "../register/Register";
import { Base } from "../core/Base";
import { any, has } from "../core/logic";
import { Component, ComponentType } from "../component/Component";
import { Destroy, Animation } from "../component/index";
import { Sampler, Channel } from "../component/animation/index";
import { Engine } from "../Engine";
/**
 * 实体类，所有的业务组件的载体。
 * @class
 * @memberof w
 */
class Entity extends Base {
  /**
   * @constructor
   * @param {String} opts.group
   * 【可选，缺省值:"default"】实体对象的组名，相同的分组会打包在一起，合理的分组能有效提高引擎的性能表现。具体逻辑:频繁更新的数据尽可能的进行分组，减少更新的范围，因为组内实体的更新会触发整个组的更新。
   * @param {Boolean} opts.disablePick
   * 【可选，缺省值:false】禁止拾取,如果设为true,即使{@link w.Engine#enablePick}。
   */
  constructor(opts = {}) {
    super(opts);
    this._type = "Entity";
    this._visibility = true;
    this._group = any(opts.group, "default");
    this._parent = null;
    this._children = [];
    this._components = [];
    this._lock = any(opts.lock, false);
    this.disablePick = any(opts.disablePick, false);
    Register.instance.entityManager.add(this);
  }
  /**
   * @property  {Boolean} lock
   * 实体对象是否锁定
   */
  get lock() {
    return this._lock;
  }
  set lock(v) {
    this._lock = v;
    return this;
  }
  /**
   * @property  {String} type
   * 【只读】实体对象的类型。
   */
  get type() {
    return this._type;
  }
  get group() {
    return this._group;
  }
  /**
   * @property  {String} group
   * 【读写】实体对象的组名，相同的分组会打包在一起，合理的分组能有效提高引擎的性能表现。具体逻辑:频繁更新的数据尽可能的进行分组，减少更新的范围，因为组内实体的更新会触发整个组的更新。
   */
  set group(v) {
    this._group = v;
    return this;
  }
  get parent() {
    return this._parent;
  }
  /**
   * @property  {String} parent
   * 【读写】实体对象的组名，相同的分组会打包在一起，合理的分组能有效提高引擎的性能表现。具体逻辑:频繁更新的数据尽可能的进行分组，减少更新的范围，因为组内实体的更新会触发整个组的更新。
   */
  set parent(v) {
    this._parent = v;
    return this;
  }
  /**
   * @property  {Array<String>} children
   * 【只读】实体对象的孩子。
   */
  get children() {
    return this._children;
  }
  /**
   * @property  {Array} components
   * 【只读】实体对象所具有的业务组件。
   */
  get components() {
    return this._components;
  }
  get visibility() {
    return this._visibility;
  }
  /**
   * @property  {Boolean} visibility
   * 【读写】实体对象可见性。
   */
  set visibility(v) {
    if (v !== this.visibility) {
      this._visibility = v;
      if (this.mesh) {
        this.mesh.visibility = v;
      }
      let board = this.getComponent(ComponentType.Board);
      if (board) {
        board.visibility = v;
      }
    }
  }
  get shine() {
    return this._shine;
  }
  /**
   * @property  {Boolean} shine
   * 【读写】实体对象发光。
   */
  set shine(v) {
    if (v !== this.shine) {
      this._shine = v;
      if (this.mesh) {
        this.mesh.shine = v;
      }
    }
  }
  /**
   * @property  {Trans} trans
   * 【只读】实体对象的Trans组件,如没有则返回undefine。
   */
  get trans() {
    return this.getComponent(ComponentType.Trans);
  }
  /**
   * @property  {Animation} trans
   * 【只读】实体对象的Animation组件,如没有则返回undefine。
   */
  get animation() {
    return this.getComponent(ComponentType.Animation);
  }
  /**
   * @property  {Mesh} mesh
   * 【只读】实体对象的Mesh组件,如没有则返回undefine。
   */
  get mesh() {
    return this.getComponent(ComponentType.Mesh);
  }
  get script() {
    return this.getComponent(ComponentType.Script);
  }
  /**
   * @property  {Pick} pick
   * 【只读】实体对象的Pick组件,如没有则返回undefine。
   */
  get pick() {
    return this.getComponent(ComponentType.Pick);
  }
  /**
   * @property  {Plot} plot
   * 【只读】实体对象的Plot组件,如没有则返回undefine。
   */
  get plot() {
    return this.getComponent(ComponentType.Plot);
  }
  /**
   * @property  {HemisLight} hemisLight
   * 【只读】实体对象的HemisLight组件,如没有则返回undefine。
   */
  get hemisLight() {
    return this.getComponent(ComponentType.HemisLight);
  }
  /**
   * @property  {AmbientLight} ambientLight
   * 【只读】实体对象的AmbientLight组件,如没有则返回undefine。
   */
  get ambientLight() {
    return this.getComponent(ComponentType.AmbientLight);
  }
  /**
   * @property  {PostProcessing} postProcessing
   * 【只读】实体对象的PostProcessing组件,如没有则返回undefine。
   */
  get postProcessing() {
    return this.getComponent(ComponentType.PostProcessing);
  }
  /**
   * @property  {PostProcessing} punctualLight
   * 【只读】实体对象的PunctualLight组件,如没有则返回undefine。
   */
  get punctualLight() {
    return this.getComponent(ComponentType.PunctualLight);
  }
  /**
   * @property  {Pick} Pick
   * 【只读】实体对象的Pick组件,如没有则返回undefine。
   */
  get pick() {
    return this.getComponent(ComponentType.Pick);
  }
  /**
   * @property  {Orbit} orbit
   * 【只读】实体对象的Orbit组件,如没有则返回undefine。
   */
  get orbit() {
    return this.getComponent(ComponentType.Orbit);
  }
  /**
   * @property  {Transform} transform
   * 【只读】实体对象的Transform组件,如没有则返回undefine。
   */
  get transform() {
    return this.getComponent(ComponentType.Transform);
  }
  /**
   * 递归设置实体的可见性。
   * @method
   * @param {Boolean} v 参数为true或false。
   */
  setVisibility(v) {
    this.visibility = v;
    for (let i = 0; i < this.children.length; i++) {
      let entityId = this.children[i];
      const clildEntity = Register.instance.entityManager.get(entityId);
      clildEntity.setVisibility(v);
    }
    return this;
  }
  /**
 * 递归设置实体的发光。
 * @method
 * @param {Boolean} v 参数为true或false。
 */
  setShine(v) {
    this.shine = v;
    for (let i = 0; i < this.children.length; i++) {
      let entityId = this.children[i];
      const clildEntity = Register.instance.entityManager.get(entityId);
      clildEntity.setShine(v);
    }
    return this;
  }
  alphato(opacity, dual = 1, easing = "none", delay = 0, finish) {
    let coms = this.getAllComponent(ComponentType.Mesh, true);
    let animation = new Animation({
      id: "alphato_" + this.id,
      loop: 1,
      keep: false,
      finalFrameCallback: finish,
    });
    for (let i = 0; i < coms.length; i++) {
      let material = coms[i].material;
      material.blending = "NormalBlending";
      let sample = new Sampler(
        [delay, delay + dual],
        [material.opacity, opacity],
        "LINEAR",
        easing
      );
      let channel = new Channel(material, "opacity");
      channel.add(sample);
      animation.add(channel);
    }
    Engine.instance.register.manager(ComponentType.Animation).add(animation);
    animation.start();
  }
  flyto(pose = {}, dual = 1, easing = "none", delay = 0, finish) {
    if (!this.trans) {
      return;
    }
    let target = this.trans;
    if (pose.instanceIndex != undefined || pose.instanceIndex != null) {
      target = this.trans._transArr[pose.instanceIndex];
    }
    let animation = new Animation({
      id: "flyto_" + this.id,
      loop: 1,
      keep: false,
      finalFrameCallback: finish,
    });

    let position = any(pose.position, target.position.toArray());
    let rotation = any(pose.rotation, [
      target.rotation.x,
      target.rotation.y,
      target.rotation.z,
    ]);
    let scale = any(pose.scale, target.scale.toArray());
    let samplePosition = new Sampler(
      [delay, delay + dual],
      [target.position.toArray(), position],
      "LINEAR",
      easing
    );
    let channelPosition = new Channel(target, "position");
    channelPosition.add(samplePosition);
    animation.add(channelPosition);

    let sampleRotation = new Sampler(
      [delay, delay + dual],
      [[target.rotation.x, target.rotation.y, target.rotation.z], rotation],
      "LINEAR",
      easing
    );
    let channelRotation = new Channel(target, "rotation");
    channelRotation.add(sampleRotation);
    animation.add(channelRotation);

    let sampleScale = new Sampler(
      [delay, delay + dual],
      [target.scale.toArray(), scale],
      "LINEAR",
      easing
    );
    let channelScale = new Channel(target, "scale");
    channelScale.add(sampleScale);
    animation.add(channelScale);
    Engine.instance.register.manager(ComponentType.Animation).add(animation);
    animation.start();
  }
  receiveShadow(flag) {
    let com = this.getAllComponent(ComponentType.Mesh, true);
    for (let i = 0; i < com.length; i++) {
      com[i].material.receiveShadow = flag;
    }
  }
  /**
   * 添加方法，添加的对象会成为该实体children中的一项。
   * @method
   * @param {Entity} entity
   * @param {Number} index
   */
  add(entity, index = 999999) {
    if (entity.parent) {
      if (entity.parent !== this.id) {
        //重新绑定新的父节点。从老的父节点清除。
        const parent = Register.instance.entityManager.get(entity.parent);
        if (parent) {
          const index = parent.children.indexOf(entity.id);
          parent.children.splice(index, 1);
        }
      }
    } else {
      entity.parent = this.id;
    }
    let loc = this.children.indexOf(entity.id);
    if (loc === -1) {
      this.children.splice(Math.min(index, this.children.length), 0, entity.id);
      this.update();
    }
    return this;
  }
  /**
   * 从孩子中找到该id对应的实体，销毁并从孩子数组中移除。
   * @method
   * @param {String} id 要删除的孩子实体id。
   */
  remove(id, destroy = true) {
    let entity = Register.instance.entityManager.get(id);
    //删除他所有的孩子
    if (entity) {
      if (destroy) {
        entity.destroy();
      } else {
        entity.removeFromParent();
        entity.update();
      }
    }
  }
  /**
   * 移除所有的孩子实体。
   * @method
   */
  removeAll(destroy = true) {
    for (let i = this.children.length - 1; i >= 0; i--) {
      let id = this.children.pop();
      this.remove(id, destroy);
    }
  }
  /**
   * 将该实体从父实体中移除（如果有父实体）。
   * @method
   */
  removeFromParent() {
    let parent = this.getParent();
    if (parent) {
      let index = parent.children.indexOf(this.id);
      parent.children.splice(index, 1);
    }
    this.parent = null;
  }
  get(id) {
    return Register.instance.entityManager.get(id);
  }
  /**
   * 获取所有的孩子实体对象。
   * @method
   * @return {Array<Entity>} 返回所有的实体集合。
   */
  getAll() {
    let entities = [];
    for (let i = 0; i < this.children.length; i++) {
      let entity = this.get(this.children[i]);
      entities.push(entity);
    }
    return entities;
  }
  getAllRecusion() {
    let out = [];
    this._getAllRecusion(out);
    return out;
  }
  _getAllRecusion(entities) {
    for (let i = 0; i < this.children.length; i++) {
      let entity = this.get(this.children[i]);
      entities.push(entity);
      entity._getAllRecusion(entities);
    }
  }
  getAllInstance() {
    let out = [];
    if (this.trans.hash == "transinstance") {
      out.push(this);
    }
    this._getAllInstance(out);
    return out;
  }
  _getAllInstance(entities) {
    for (let i = 0; i < this.children.length; i++) {
      let entity = this.get(this.children[i]);
      if (entity.trans.hash == "transinstance") {
        entities.push(entity);
      }
      entity._getAllInstance(entities);
    }
  }
  /**
   * 获取父实体对象。
   * @method
   * @return {Array<Entity>} 如果有则返回父实体，否则返回false。
   */
  getParent() {
    if (this.parent) {
      return Register.instance.entityManager.get(this.parent);
    }
    return false;
  }
  checkComponent(component) {
    if (!(component instanceof Component)) {
      throw new Error(`不是有效组件类型!`);
    }
  }
  /**
   * 添加组件
   * @param {Component} component 组件对象
   */
  addComponent(component) {
    this.checkComponent(component);
    const { type, id } = component;
    let oldCom = Register.instance.manager(type).get(id); //删除已存在动画。
    Register.instance.manager(type).add(component); //添加到组件管理类中
    component.attach(this.id);
    this.components[type] = id; //写入实体组件标识中
    if (oldCom) oldCom.destroy();
  }
  /**
   * 移除组件
   * @param {Component} component 组件对象
   */
  removeComponent(type) {
    let component = this.getComponent(type);
    if (component) {
      this.components[type] = undefined;
      component.deactive();
      component.detach(this.id);
      let flag = component.destroy();
      if (flag) {
        component = null;
      }
    }
  }
  /**
   * 移除全部组件
   * @param {Component} component 组件对象
   */
  removeAllComponent() {
    for (let i = this.components.length - 1; i >= 0; i--) {
      this.removeComponent(i);
    }
    this._components = null;
  }
  /**
   * 获取组件
   * @param {Number} type 组件类型。
   */
  getComponent(type) {
    if (!this.components) {
      return null;
    }
    const cid = this.components[type];
    if (cid) {
      return Register.instance.manager(type).get(cid);
    }
    return null;
  }
  /**
   * 获取组件是否存在
   * @param {Number} type 组件类型。
   */
  hasComponent(type) {
    return this.components[type];
  }
  /**
   * 获取所有组件
   */
  getAllComponent(type, recusion = false) {
    let components = [];
    if (type == undefined) {
      for (let i = 0; i < this.components.length; i++) {
        if (this.components[i]) {
          components.push(this.getComponent(i));
        }
      }
    } else {
      let com = this.getComponent(type);
      if (com) {
        components.push(com);
      }
    }
    if (recusion) {
      let all = this.getAllRecusion(true);
      for (let i = 0; i < all.length; i++) {
        let c = all[i].getComponent(type);
        if (c) {
          components.push(c);
        }
      }
    }
    return components;
  }

  findRoot(entity) {
    if (!entity) {
      // debugger
    }
    if (!has(entity.parent)) {
      return entity;
    } else {
      let parentEntity = this.get(entity.parent);
      return this.findRoot(parentEntity);
    }
  }
  /**
   * 获取实体的根节点。
   */
  getRoot() {
    return this.findRoot(this);
  }
  /**
   * 销毁实体
   */
  destroy(task = false, isTop) {
    if (task) {
      this.addComponent(new Destroy());
    } else {
      if (isTop) {
        const destroyCom = this.getComponent(ComponentType.Destroy);
        if (!destroyCom) {
          return;
        }
      }
      this.removeFromParent(); //从父亲中删除
      Register.instance.entityManager.remove(this.id); //删除本身
      this.removeAllComponent();
      this.removeAll(); //删除所有孩子
      super.destroy();
      this._group = null;
      this._id = null;
    }
  }
  update() {
    //再实体添加到group中调用。
    //本身更新
    const coms = this.getAllComponent();
    for (let i = 0; i < coms.length; i++) {
      coms[i].update();
    }
    //孩子更新
    const children = this.getAll();
    for (let i = 0; i < children.length; i++) {
      children[i].update();
    }
  }
  get skinAnmations() {
    return this._skinAnmations;
  }

  set skinAnmations(v) {
    this._skinAnmations = v;
  }
  startSkin() {
    for (let index = 0; index < this.skinAnmations.length; index++) {
      const skinAnmation = this.skinAnmations[index];
      skinAnmation.start();
    }
  }
  stopSkin() {
    for (let index = 0; index < this.skinAnmations.length; index++) {
      const skinAnmation = this.skinAnmations[index];
      skinAnmation.stop();
    }
  }
}
export { Entity };
